import { ActivityRecord, ByteArray, Dealer, EntityIdentifier, ExecutionRecord, ExecutiveRecord, LockRecord, StateRecord, StatusRecord, UserIdentifier } from "./entity"
import { ActivityRepository } from "./repository"
import { EnumConverter, Serializer } from "./serializer"
import { STATE_RECORD,VARIABLE,INPUT,OUTPUT,ERROR,STATUS, CONTEXT, REQUEST, DEALER, ACTIVITY_STATUS, EXECUTE_STATUS, GRAPH, AUTH, ENTER, DEAL, EXIT, REQUEST_ID } from "./shared-symbols"
import { Graph, GraphAction } from "./graph"
import { UniqueIdentifier } from "./store"


//#region State / StateObject

export interface StateContext{
  serializer:Serializer
}
/**
 * a group of data
 * the activity executing indeed is transfering from one state to another state
 *
 * @export
 * @interface State
 */
export  interface State{
  $IN:Readonly<unknown>
  $VAR:Readonly<Record<string,unknown>>
  $OUT:Readonly<unknown>
  $ERR:Readonly<unknown>
  $SUP:Readonly<State>
  [key:string]:Readonly<unknown>
}

export interface StateObject<
  TActivity extends StateObject<TActivity,TState>,
  TState extends State=State,
  TRecord extends StateRecord=StateRecord,
  TContext extends StateContext=StateContext
>
{
  parent?:TActivity
  input:unknown
  variable:Readonly<Record<string,unknown>>
  output:unknown
  error:unknown
  state: TState
  context:TContext

  [STATE_RECORD]:TRecord
  [INPUT]?:unknown
  [VARIABLE]?:Record<string|symbol,unknown>
  [OUTPUT]?:unknown
  [ERROR]?:unknown
  [CONTEXT]:TContext
}
//#endregion state/stateObject

//#region request / executiveContext

export interface ActivityRequest {
  requestId: UniqueIdentifier
  dealer: Dealer
  input?: unknown
  activityId?: EntityIdentifier
  flowId?: EntityIdentifier
}

export interface Calendar {
  now():Date
}
export class DefaultCalendar implements Calendar {
  private _now?:Date
  now(): Date {
    return this._now??new Date()
  }
  setNow(now:Date):void {
    this._now = now
  }
}

export interface LockManager {
  lock(requestId: EntityIdentifier, activityId: EntityIdentifier, dealerId: UserIdentifier,now:Date, milliseconds: number): Promise<LockRecord>
  unlock(activityId: EntityIdentifier, dealerId: UserIdentifier): Promise<boolean>
}


export interface Autherizable {
  owner:string
}
export interface Autherizer {
  authorize(activity: Autherizable,dealer: Dealer|undefined): Promise<boolean>
}

export class DefaultAutherizer implements Autherizer {
  authorize(activity: Autherizable,dealer: Dealer|undefined): Promise<boolean> {
    if(activity.owner===undefined) return Promise.resolve(true)
    return Promise.resolve(activity.owner===dealer?.id)
  }
}

export interface Assigner {
  merge(target:unknown,source:unknown):unknown
  assign(target:unknown,source:unknown):unknown
}

export enum ActionType {
  AUTH,
  ENTER,
  DEAL,
  EXIT
}
export interface ActionFactory {
  getAction(name:string,actionType:ActionType,activity:Activity):GraphAction
}

export interface ExecutiveContext extends StateContext {
  request:ActivityRequest
  requestId:UniqueIdentifier
  input:unknown
  dealer:Dealer
  enumConverter:EnumConverter
  repository:ActivityRepository
  calendar:Calendar
  
  autherizer:Autherizer
  assigner:Assigner
  actionFactory:ActionFactory
  lockTimeout?:number
  [REQUEST]:ActivityRequest
  [DEALER]:Dealer
  [REQUEST_ID]:UniqueIdentifier
}
//#endregion request / context

//#region executive

/**
 *the status of activity
 *
 * @export
 * @enum {number}
 */
export enum ActivityStatus{
  
  /**
   * initialized
   *  - initialized the variable from parent/routing
   *  - has a corresponding activity record in the database
   */
  INITIALIZED,
  
  /**
   * entered
   *  - the first time to enter the activity
   *  - often raise an ENTER event
   */
  ENTERED,
  
  /**
   * dealing    
   *  - the activity is dealing with the input
   */
  DEALING,
  
  /**
   * suspended
   *  - the activity is suspended(waiting for the next execution)
   *  - this will record the result 
   */
  SUSPENDED,
  
  /**
   * done
   *  - the activity is dealed  successfully
   *  - this will generate the output and the result
   */
  DONE,
  
  /**
   * exited
   *  - the activity is exited
   *  - raise an EXIT event
   */
  EXITED, 

  /**
   * COMPLETED
   *  - the activity is finished
   *  - the parent routed this activity's nexts, then mark this activity as finished
   */
  COMPLETED
}


/**
 * every time the activity is executed, it will be in one of the following statuses
 *
 * @export
 * @enum {number}
 */
export enum ExecuteStatus{
  
  /**
   * unknown
   */
  UNKNOWN,
  
  /**
   * - the startAction is invoked 
   * - the actionRecord is created in the database
   */
  STARTED,
  
  /**
   * - the activity is go through the actions one by one
   */
  RUNNING,
  
  /**
   * - the activity is finished successfully
   */
  FINISHED,
  
  /**
   * some error occurred,
   * but can re-execute the activity
   */
  FAILED,
  
  /**
   * some error occurred,
   * but can not re-execute the activity
   */
  ERROR,
  
  /**
   * some error occurred,
   * break all the flow's execution
   */
  EXCEPTION,
  
  /**
   * ABORTED
   * some one ask to abort the activity
   */
  ABORTED
}



export interface ExecuteResult
{
  activityStatus:ActivityStatus
  executeStatus:ExecuteStatus
  returnValue:unknown
}

export type ExecutiveAction<TContext extends ExecutiveContext> = (context:TContext)=>unknown

/**
 * the object of the activity
 *
 * @export
 * @interface ExecutiveObject
 */
export interface Executive<
  TActivity extends Executive<TActivity,TState,TRecord,TContext>,
  TState extends State=State,
  TRecord extends ExecutiveRecord=ExecutiveRecord,
  TContext extends ExecutiveContext=ExecutiveContext
> extends StateObject<TActivity,TState,TRecord,TContext>
{
  activityStatus:ActivityStatus
  executeStatus:ExecuteStatus
  graph:Readonly<Graph>

  [ACTIVITY_STATUS]:ActivityStatus
  [EXECUTE_STATUS]:ExecuteStatus
  [GRAPH]:Graph
  // execute(context:TContext):Promise<ExecuteResult>
  AUTH?:ExecutiveAction<TContext>
  ENTER?:ExecutiveAction<TContext>
  DEAL?:ExecutiveAction<TContext>
  EXIT?:ExecutiveAction<TContext>
}
//#endregion executive

//#region actions

export interface Action
<
  TActivity extends Executive<TActivity,TState,TRecord,TContext>,
  TState extends State=State,
  TRecord extends ExecutiveRecord=ExecutiveRecord,
  TContext extends ExecutiveContext=ExecutiveContext
>
{

  activity:TActivity
  activityRecord:TRecord
  executionRecord?:ExecutionRecord
  lockRecord?:LockRecord
  result?:unknown
  previous?:Action<TActivity,TState,TRecord,TContext>
  next?:Action<TActivity,TState,TRecord,TContext>|null
  invoke(context:TContext):Promise<Action<TActivity,TState,TRecord,TContext>|null|undefined>
}


//#endregion actions

//#region activity

export interface ActivityContext extends ExecutiveContext {
  
  flow?: Activity
}
export interface ActivityCreationInfo  {
  graph?:unknown
  parent?:Activity<State>
  activityRecord?:ActivityRecord
  context?:ActivityContext
  flowRecords?:ActivityRecord[]
}

export interface Activity
<
  TState extends State=State
> extends Executive<Activity<TState>,TState,ActivityRecord,ActivityContext>,Autherizable
{
  id:EntityIdentifier
  name:string
  path:string
  flowId:EntityIdentifier
  parentId:EntityIdentifier
  context:ActivityContext
  flow:Activity
}

//#endregion activity


